﻿#include "qquickflatwindowhandler.h"
#include "qquickflatwindowhandler_p.h"

#include <QQuickItem>

// class QQuickFlatWindowHandler

QQuickFlatWindowHandler::QQuickFlatWindowHandler(QObject *parent)
    : QObject(parent)
    , d_ptr(new QQuickFlatWindowHandlerPrivate())
{
}

QQuickFlatWindowHandler::~QQuickFlatWindowHandler()
{
}

void QQuickFlatWindowHandler::classBegin()
{
}

void QQuickFlatWindowHandler::componentComplete()
{
    Q_D(QQuickFlatWindowHandler);

    auto obj = parent();
    while (nullptr != obj) {
        if (obj->inherits("QQuickRootItem")) {
            if (auto rootItem = qobject_cast<QQuickItem *>(obj)) {
                if (auto window = rootItem->window()) {
                    d->window = window;

                    d->helper = new QNativeWindowHelper(window, d);
                    connect(d->helper, &QNativeWindowHelper::scaleFactorChanged,
                            this, &QQuickFlatWindowHandler::scaleFactorChanged);
                    if (!qFuzzyCompare(d->helper->scaleFactor(), 1.0)) {
                        emit scaleFactorChanged();
                    }
                }

                break;
            }
        }

        obj = obj->parent();
    }
}

void QQuickFlatWindowHandler::setBothDraggableMargin(int dm)
{
    Q_D(QQuickFlatWindowHandler);

    if (dm != d->priDraggableMargins.top()) {
        d->priDraggableMargins.setTop(dm);
        emit topDraggableMarginChanged();
    }
    if (dm != d->priDraggableMargins.bottom()) {
        d->priDraggableMargins.setBottom(dm);
        emit bottomDraggableMarginChanged();
    }
    if (dm != d->priDraggableMargins.left()) {
        d->priDraggableMargins.setLeft(dm);
        emit leftDraggableMarginChanged();
    }
    if (dm != d->priDraggableMargins.right()) {
        d->priDraggableMargins.setRight(dm);
        emit rightDraggableMarginChanged();
    }
}

void QQuickFlatWindowHandler::setTopDraggableMargin(int dm)
{
    Q_D(QQuickFlatWindowHandler);

    if (dm != d->priDraggableMargins.top()) {
        d->priDraggableMargins.setTop(dm);
        emit topDraggableMarginChanged();
    }
}

void QQuickFlatWindowHandler::setLeftDraggableMargin(int dm)
{
    Q_D(QQuickFlatWindowHandler);

    if (dm != d->priDraggableMargins.left()) {
        d->priDraggableMargins.setLeft(dm);
        emit leftDraggableMarginChanged();
    }
}

void QQuickFlatWindowHandler::setRightDraggableMargin(int dm)
{
    Q_D(QQuickFlatWindowHandler);

    if (dm != d->priDraggableMargins.right()) {
        d->priDraggableMargins.setRight(dm);
        emit rightDraggableMarginChanged();
    }
}

void QQuickFlatWindowHandler::setBottomDraggableMargin(int dm)
{
    Q_D(QQuickFlatWindowHandler);

    if (dm != d->priDraggableMargins.bottom()) {
        d->priDraggableMargins.setBottom(dm);
        emit bottomDraggableMarginChanged();
    }
}

int QQuickFlatWindowHandler::topDraggableMargin() const
{
    Q_D(const QQuickFlatWindowHandler);

    return d->priDraggableMargins.top();
}

int QQuickFlatWindowHandler::leftDraggableMargin() const
{
    Q_D(const QQuickFlatWindowHandler);

    return d->priDraggableMargins.left();
}

int QQuickFlatWindowHandler::rightDraggableMargin() const
{
    Q_D(const QQuickFlatWindowHandler);

    return d->priDraggableMargins.right();
}

int QQuickFlatWindowHandler::bottomDraggableMargin() const
{
    Q_D(const QQuickFlatWindowHandler);

    return d->priDraggableMargins.bottom();
}

void QQuickFlatWindowHandler::setTopMaximizedMargin(int dm)
{
    Q_D(QQuickFlatWindowHandler);

    if (dm != d->priMaximizedMargins.top()) {
        d->priMaximizedMargins.setTop(dm);
        emit topMaximizedMarginChanged();
    }
}

void QQuickFlatWindowHandler::setLeftMaximizedMargin(int dm)
{
    Q_D(QQuickFlatWindowHandler);

    if (dm != d->priMaximizedMargins.left()) {
        d->priMaximizedMargins.setLeft(dm);
        emit leftMaximizedMarginChanged();
    }
}

void QQuickFlatWindowHandler::setRightMaximizedMargin(int dm)
{
    Q_D(QQuickFlatWindowHandler);

    if (dm != d->priMaximizedMargins.right()) {
        d->priMaximizedMargins.setRight(dm);
        emit rightMaximizedMarginChanged();
    }
}

void QQuickFlatWindowHandler::setBottomMaximizedMargin(int dm)
{
    Q_D(QQuickFlatWindowHandler);

    if (dm != d->priMaximizedMargins.bottom()) {
        d->priMaximizedMargins.setBottom(dm);
        emit bottomMaximizedMarginChanged();
    }
}

int QQuickFlatWindowHandler::topMaximizedMargin() const
{
    Q_D(const QQuickFlatWindowHandler);

    return d->priMaximizedMargins.top();
}

int QQuickFlatWindowHandler::leftMaximizedMargin() const
{
    Q_D(const QQuickFlatWindowHandler);

    return d->priMaximizedMargins.left();
}

int QQuickFlatWindowHandler::rightMaximizedMargin() const
{
    Q_D(const QQuickFlatWindowHandler);

    return d->priMaximizedMargins.right();
}

int QQuickFlatWindowHandler::bottomMaximizedMargin() const
{
    Q_D(const QQuickFlatWindowHandler);

    return d->priMaximizedMargins.bottom();
}

void QQuickFlatWindowHandler::addIncludeItem(QQuickItem *item)
{
    Q_D(QQuickFlatWindowHandler);

    d->includeItems.insert(item);
}

void QQuickFlatWindowHandler::removeIncludeItem(QQuickItem *item)
{
    Q_D(QQuickFlatWindowHandler);

    d->includeItems.remove(item);
}

void QQuickFlatWindowHandler::addExcludeItem(QQuickItem *item)
{
    Q_D(QQuickFlatWindowHandler);

    d->excludeItems.insert(item);
}

void QQuickFlatWindowHandler::removeExcludeItem(QQuickItem *item)
{
    Q_D(QQuickFlatWindowHandler);

    d->excludeItems.remove(item);
}

void QQuickFlatWindowHandler::setDragableHeight(int value)
{
    Q_D(QQuickFlatWindowHandler);

    if (value != d->dragableHeight) {
        d->dragableHeight = value;
        emit dragableHeightChanged();
    }
}

void QQuickFlatWindowHandler::addExcludeRect(QRect rect)
{
    Q_D(QQuickFlatWindowHandler);

    d->excludeRects.push_back(rect);
}

int QQuickFlatWindowHandler::dragableHeight() const
{
    Q_D(const QQuickFlatWindowHandler);

    return d->dragableHeight;
}

qreal QQuickFlatWindowHandler::scaleFactor() const
{
    Q_D(const QQuickFlatWindowHandler);

    return d->helper ? d->helper->scaleFactor() : 1.0;
}

void QQuickFlatWindowHandler::minimizeWindow()
{
    Q_D(QQuickFlatWindowHandler);

    if (d->window) {
        auto oldStates = d->window->windowStates();
        d->window->setWindowStates((oldStates & ~Qt::WindowActive) | Qt::WindowMinimized);
    }
}

void QQuickFlatWindowHandler::maximizeWindow()
{
    Q_D(QQuickFlatWindowHandler);

    if (d->window) {
        if (QWindow::Maximized == d->window->visibility()) {
            d->window->showNormal();
        } else {
            d->window->showMaximized();
        }
    }
}

void QQuickFlatWindowHandler::closeWindow()
{
    Q_D(QQuickFlatWindowHandler);

    if (d->window) {
        d->window->close();
    }
}


// class QQuickFlatWindowHandlerPrivate

QQuickFlatWindowHandlerPrivate::QQuickFlatWindowHandlerPrivate()
    : window(nullptr)
    , helper(nullptr)
    , dragableHeight(0)
{
}

QQuickFlatWindowHandlerPrivate::~QQuickFlatWindowHandlerPrivate()
{
}

QMargins QQuickFlatWindowHandlerPrivate::draggableMargins() const
{
    return priDraggableMargins;
}

QMargins QQuickFlatWindowHandlerPrivate::maximizedMargins() const
{
    return priMaximizedMargins;
}


/*!
    Check whether the point \a pos is in the response area.Returns true
    if correct, otherwise returns false.

    \note The response area is the area, which needs to handle dragging,
    maximizing, minimizing, etc, when the user clicks on it.
*/
bool QQuickFlatWindowHandlerPrivate::hitTest(const QPoint &pos) const
{
    int scaledTitleBarHeight = dragableHeight * helper->scaleFactor();

    //Check if the point is in unresponsive region
    for(int i=0;i<excludeRects.count();i++){
        if(excludeRects.at(i).contains(pos)){
            return false;
        }
    }

    //Check if the point is in excludeItems area
    if (!window)
        return false;
    else if (scaledTitleBarHeight == 0)
        return false;
    else if ((scaledTitleBarHeight > 0)
             && (pos.y() >= scaledTitleBarHeight))
        return false;

    auto rootItem = window->contentItem();
    if (nullptr == rootItem) {
        return false;
    }

    int currentIndex = -1;
    QQuickItem *items[32] = {0};
    auto child = rootItem;
    QPointF p = pos;

    while (child && (currentIndex < 31)) {
        items[++currentIndex] = child;
        auto grandchild = child->childAt(p.x(), p.y());
        if (nullptr == grandchild) {
            break;
        }

        p = child->mapToItem(grandchild, p);
        child = grandchild;
    }

    while (currentIndex > 0) {
        child = items[currentIndex];
        --currentIndex;

        if (includeItems.contains(child)) {
            break;
        } else if (excludeItems.contains(child)) {
            return false;
        } else if (rootItem == child) {
            break;
        }
    }

    return true;
}
